package com.reyco1.medusa.abstract
{
	import com.reyco1.medusa.debug.Debugger;
	
	import flash.events.EventDispatcher;
	import flash.events.IEventDispatcher;
	import flash.utils.Dictionary;
	
	public class SmartEventDispatcher extends EventDispatcher
	{
		private var _numListeners:int = 0;
		private var _listeners:Array = [];
		
		public function SmartEventDispatcher(target:IEventDispatcher=null)
		{
			super(target);
		}
		
		/* methods */
		
		override public function addEventListener(type:String, listener:Function, useCapture:Boolean=false, priority:int=0, useWeakReference:Boolean=false):void
		{
			super.addEventListener(type, listener, useCapture, priority, useWeakReference);
			
			var capture:int = int(useCapture);
			
			if (capture in _listeners == false)
			{
				_listeners[capture] = {};
			}
			
			if (type in _listeners[capture] == false)
			{
				_listeners[capture][type] = new Dictionary(true);
			}
			
			if (listener in _listeners[capture][type] == false)
			{
				_listeners[capture][type][listener] = true;
				_numListeners++;
			}
		}
		
		override public function removeEventListener(type:String, listener:Function, useCapture:Boolean=false):void
		{
			super.removeEventListener(type, listener, useCapture);
			
			try 
			{
				var capture:int = int(useCapture);
				
				if (delete _listeners[capture][type][listener])
				{
					_numListeners--;
					
					var cleanObject:Boolean;
					var key:*;
					
					cleanObject = true;
					
					for (key in _listeners[capture][type])
					{
						cleanObject = false; 
						break;
					}
					
					if (cleanObject) delete _listeners[capture][type];
					
					cleanObject = true;
					
					for (key in _listeners[capture])
					{
						cleanObject = false; 
						break;
					}
					
					if (cleanObject) delete _listeners[capture];
				}				
			}
			catch(err:Error)
			{
				// does not exist, fail silently
			}
		}
		
		public function removeAllListeners(type:String = null):void 
		{
			var capture:*; 
			var etype:String;
			var listener:*;
			var types:Object;
			var listeners:Dictionary;
			
			for (capture in _listeners)
			{				
				types = _listeners[int(capture)];
				
				for (etype in types)
				{					
					if (!type || type == etype) 
					{
						listeners = types[etype];
						for (listener in listeners)
						{
							try 
							{
								super.removeEventListener(etype, listener, Boolean(capture));
							}
							catch(err:Error)
							{
								// likely an argument error, but that really
								// shouldn't happen; this is just in cases
							}
							
							delete listeners[listener];
							_numListeners--;
						}
						
						delete types[etype];
					}
				}
				
				if (type == null || _numListeners == 0) delete _listeners[int(capture)];
			}
		}
		
		public function log(outputItem:*, type:String = null):void
		{
			Debugger.log(outputItem, this, type);
		}
		
		/* properties */

		public function get numListeners():int
		{
			return _numListeners;
		}

		public function get listeners():Array
		{
			return _listeners;
		}

	}
}